// N1String.cpp: implementation of the N1String class.
//
//////////////////////////////////////////////////////////////////////

#include "N1String.h"
#include "N1Exception.h"
#include "N1Math.h"
#include "N1Unit.h"

static char* _gEmptyString = "";

N1String::N1String() :m_pStr(_gEmptyString), m_nRealCount(0) 
{
};

N1String::N1String(const N1StringChar* str)
{  
    m_nRealCount = strlen(str) + 1;
    m_pStr = alloc( m_nRealCount );
    strcpy(m_pStr, str);
};

N1String::N1String(const N1String &str)
{  
    m_nRealCount = str.m_nRealCount;
    m_pStr = alloc( m_nRealCount );
    strcpy(m_pStr, str.m_pStr);
};

N1String::~N1String() 
{
    if(m_nRealCount)
    {
        N1Free(m_pStr);
    }
};

N1NAME_DEF(N1String)


N1UInt32 N1String::length() const 
{
    return strlen(m_pStr);
}


void N1String::setLength(N1UInt32 len)
{
    N1StringChar *pStr = alloc(len);
    memcpy(pStr, m_pStr, sizeof(N1StringChar) * N1Min(len, strlen(m_pStr)+1));
    if(m_nRealCount)
    {
        N1Free(m_pStr);
    }
    m_pStr = pStr;
    m_pStr[len - 1] = 0;
    m_nRealCount = len;
}

char N1String::at(N1UInt32 index) const 
{
    return m_pStr[index];
}

bool N1String::operator==(const N1String& str) const 
{
    return strcmp(m_pStr, str.m_pStr)==0;
}

bool N1String::operator==(const N1StringChar* str) const 
{
    if(!str)
    {
        if (*m_pStr == 0)
        {
            return true;
        }
        return false;
    }

    return strcmp(m_pStr, str)==0;
}

void N1String::formatv(const N1StringChar* format, va_list args)
{
    int size = vsnprintf(NULL, 0, format, args);
    if(size>0)
    {
        setLength(size + 1);
        vsnprintf(m_pStr, size+1, format, args);
    } else N1Exception::Throw("N1String::formatv: n1vsnprintf returned %d", size);
}

void N1String::format(const N1StringChar* format, ...)
{
    va_list argList;
    va_start( argList, format );

    formatv(format, argList);

    va_end(argList);
}

void N1String::releaseBuffer()
{
    uint32 len = strlen(m_pStr);
    setLength(len + 1);
}

void N1String::expandBuffer(uint32 size)
{
    if(m_nRealCount<size)
    {
        uint32 newLen = N1GetNewSizeByLog2(m_nRealCount, size - m_nRealCount, 1);
        setLength(newLen);
    }
}

const N1String& N1String::operator+=(N1StringChar *str)
{
    expandBuffer(length() + strlen(str) + 1);
    strcat(m_pStr, str);
    return *this;
}

const N1String::N1StringType& N1String::operator+=(N1StringChar c)
{
    uint32 strLen = length();

    expandBuffer(strLen + 1 + 1);

    N1StringChar* ptr = m_pStr + strLen;

    *ptr++ = c;
    *ptr = 0;

    return *this;
}

#ifdef N1LIB_SELFTEST

N1TEST(N1StringTestConstructors)
{
    N1AnsiString s1;
    N1AnsiString s2("Ansi constructon");

    //N1UnicodeString u1;
    //N1UnicodeString u2(L"Unicode constructon");
}

N1TEST(N1StringTestOperatorCharPtr)
{
    const char str1[] = "Ansi string";
    //const wchar_t str2[] = L"Unicode string";

    N1AnsiString s1(str1);
    //N1UnicodeString u1(str2);

    N1CHECK(strcmp(s1.str(), str1)==0, 1);
    //N1CHECK(wcscmp(u1.str(), str2)==0, 2);
}

N1TEST(N1StringTestLength)
{
    const char str1[] = "1234567";
    //const wchar_t str2[] = L"12345678";

    N1AnsiString s1(str1);
    N1AnsiString s2;
    //N1UnicodeString u1(str2);
    //N1UnicodeString u2;

    N1CHECK(s1.length()==7, 1);
    N1CHECK(s2.length()==0, 2);

    //N1CHECK(u1.length()==8, 10);
    //N1CHECK(u2.length()==0, 11);
}

N1TEST(N1StringTestOperatorBrackets)
{
    const char str1[] = "Ansi string";
    //const wchar_t str2[] = L"Unicode string";

    N1AnsiString s1(str1);
    //N1UnicodeString<wchar_t> u1(str2);

    uint32 i;

    for(i=0;i<s1.length();i++)
    {
	    N1CHECK(s1[i]==str1[i], i + 1);
    }

/*
    for(i=0;i<u1.length();i++)
    {
	    N1CHECK(u1[i]==str2[i], i + 21);
    }*/
}

N1TEST(N1StringCopyConstructors)
{
    N1AnsiString s2("Ansi constructon");
    N1AnsiString s3(s2);

    //N1String<wchar_t> u2(L"Unicode constructon");
    //N1String<wchar_t> u3(u2);

    uint32 i;

    for(i=0;i<s2.length();i++)
    {
	    N1CHECK(s2[i]==s3[i], i + 1);
    }
/*
    for(i=0;i<u2.length();i++)
    {
	    N1CHECK(u2[i]==u3[i], i + 21);
    }*/
}

N1TEST(N1StringFormat)
{
    N1AnsiString s2("Test message 1 2 msg");
    N1AnsiString s3;

    //N1UnicodeString u2(L"Test message 1 2 msg");
    //N1UnicodeString u3();

    s3.format("Test message %d %u %s", 1, 2, "msg");
    //u3.format(L"Test message %d %u %s", 1, 2, "msg");

    N1CHECK(s3==s2, 1);
    //N1CHECK(u3==u2, 2);
}

N1TEST(N1StringOperatorAddChar)
{
    N1AnsiString s1("ABC 45");
    N1AnsiString s2;

    s2 += 'A';
    s2 += 'B';
    s2 += 'C';
    s2 += ' ';
    s2 += '4';
    s2 += '5';

    N1CHECK(s1==s2, 1);
}

#endif //N1LIB_SELFTEST
